﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Text;

namespace Mal.Balades.Admin.Net
{
    public delegate void DownloadStringCompletedEventHandler(object sender, DownloadStringCompletedEventArgs e);

    public class HttpWebRequest
    {
        #region enum, struct, class
        private enum Method
        {
            POST,
            GET
        }

        private class HttpWebRequestState
        {
            public HttpWebRequestState()
            {
                this.Request = null;
            }

            public HttpWebRequestState(System.Net.HttpWebRequest request, string data, object userState)
            {
                this.Request = request;
                this.Data = data;
                this.UserState = userState;
            }

            public HttpWebRequestState(System.Net.HttpWebRequest request, string data, Dictionary<string, string> headers, Method method, object userState)
                : this(request, data, userState)
            {
                this.Headers = headers;
                this.Method = method;
            }

            public System.Net.HttpWebRequest Request { get; set; }

            public string Data { get; set; }

            public object UserState { get; set; }

            public Dictionary<string, string> Headers { get; set; }

            public Method Method { get; set; }
        }
        #endregion

        #region Properties
        public event DownloadStringCompletedEventHandler DownloadStringCompleted;
        #endregion

        #region Methods
        public void GetRequestAsync(Uri uri, Dictionary<string, string> datas)
        {
            this.GetRequestAsync(uri, datas, null, null);
        }

        public void GetRequestAsync(Uri uri, Dictionary<string, string> datas, object userState)
        {
            this.GetRequestAsync(uri, datas, null, userState);
        }

        public void GetRequestAsync(Uri uri, Dictionary<string, string> datas, Dictionary<string, string> headers)
        {
            this.GetRequestAsync(uri, datas, headers, null);
        }

        public void GetRequestAsync(Uri uri, Dictionary<string, string> datas, Dictionary<string, string> headers, object userState)
        {
            this.DownloadStringAsync(uri, datas, headers, Method.GET, userState);
        }

        public void PostRequestAsync(Uri uri, Dictionary<string, string> datas)
        {
            this.PostRequestAsync(uri, datas, null, null);
        }

        public void PostRequestAsync(Uri uri, Dictionary<string, string> datas, object userState)
        {
            this.PostRequestAsync(uri, datas, null, userState);
        }

        public void PostRequestAsync(Uri uri, Dictionary<string, string> datas, Dictionary<string, string> headers)
        {
            this.PostRequestAsync(uri, datas, headers, null);
        }

        public void PostRequestAsync(Uri uri, Dictionary<string, string> datas
                                    , Dictionary<string, string> headers, object userState)
        {
            this.DownloadStringAsync(uri, datas, headers, Method.POST, userState);
        }

        private void DownloadStringAsync(Uri uri, Dictionary<string, string> datas
            , Dictionary<string, string> headers
            , Method method, object userState)
        {
            string data = null;

            if (datas != null && datas.Count > 0)
            {
                StringBuilder dataStringBuilder = null;

                foreach (KeyValuePair<string, string> item in datas)
                {
                    if (dataStringBuilder == null)
                    {
                        dataStringBuilder = new StringBuilder();
                        dataStringBuilder.AppendFormat("{0}={1}", item.Key, item.Value);
                    }
                    else
                    {
                        dataStringBuilder.AppendFormat("&{0}={1}", item.Key, item.Value);
                    }
                }

                data = dataStringBuilder.ToString();
            }

            if (method == Method.GET && !string.IsNullOrEmpty(data))
            {
                string url = string.Format("{0}?{1}", uri.AbsoluteUri, data);
                uri = new Uri(url, UriKind.Absolute);
            }

            System.Net.HttpWebRequest webRequest = (System.Net.HttpWebRequest)WebRequest.Create(uri);
            webRequest.Method = method.ToString();

            if (headers != null)
            {
                foreach (KeyValuePair<string, string> item in headers)
                {
                    webRequest.Headers[item.Key] = item.Value;
                }
            }

            if (method == Method.POST)
            {
                webRequest.ContentType = "application/x-www-form-urlencoded";
            }

            HttpWebRequestState state = new HttpWebRequestState(webRequest, data, headers, method, userState);

            // Start the request
            if (method == Method.POST)
            {
                webRequest.BeginGetRequestStream(new AsyncCallback(this.GetRequestStreamCallback), state);
            }
            else
            {
                webRequest.BeginGetResponse(new AsyncCallback(this.GetResponseCallback), state);
            }
        }

        private void RaiseDownloadStringCompleted(DownloadStringCompletedEventArgs e)
        {
            if (this.DownloadStringCompleted != null)
            {
                this.DownloadStringCompleted(this, e);
            }
        }
        #endregion

        #region Events
        private void GetRequestStreamCallback(IAsyncResult asynchronousResult)
        {
            HttpWebRequestState state = (HttpWebRequestState)asynchronousResult.AsyncState;
            System.Net.HttpWebRequest webRequest = state.Request;

            // End the stream request operation 
            // Start the request
            if (state.Method == Method.POST)
            {
                Stream postStream = webRequest.EndGetRequestStream(asynchronousResult);

                if (!string.IsNullOrEmpty(state.Data))
                {
                    byte[] byteArray = Encoding.UTF8.GetBytes(state.Data);

                    // Add the post data to the web request 
                    postStream.Write(byteArray, 0, byteArray.Length);
                    postStream.Close();
                }
            }

            // Start the web request 
            webRequest.BeginGetResponse(new AsyncCallback(this.GetResponseCallback), state);
        }

        private void GetResponseCallback(IAsyncResult asynchronousResult)
        {
            HttpWebRequestState state = (HttpWebRequestState)asynchronousResult.AsyncState;

            try
            {
                System.Net.HttpWebRequest webRequest = state.Request;
                HttpWebResponse response;

                // End the get response operation 
                response = (HttpWebResponse)webRequest.EndGetResponse(asynchronousResult);
                Stream streamResponse = response.GetResponseStream();
                StreamReader streamReader = new StreamReader(streamResponse);
                string responseText = streamReader.ReadToEnd();
                streamResponse.Close();
                streamReader.Close();
                response.Close();

                this.RaiseDownloadStringCompleted(new DownloadStringCompletedEventArgs(responseText, state.UserState));
            }
            catch (WebException e)
            {
                this.RaiseDownloadStringCompleted(new DownloadStringCompletedEventArgs(e, state.UserState));
            }
            catch (Exception exp)
            {
                this.RaiseDownloadStringCompleted(new DownloadStringCompletedEventArgs(exp, state.UserState));
            }
        }
        #endregion
    }
}